﻿using DevelopTool.Common;
using Nancy.Hosting.Self;
using NetworkCommsDotNet;
using NetworkCommsDotNet.Connections;
using NetworkCommsDotNet.Connections.UDP;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;

namespace DevelopTool.Bll.ChatBlls
{
    /// <summary>
    /// 聊天网络相关业务
    /// </summary>
    public static class ChatNetworkBll
    {
        /// <summary>
        /// 发送网络广播事件委托
        /// </summary>
        /// <param name="chatUser"></param>
        public delegate void SendDiscoveryBroadcastEventHandler(bool result, string message);


        /// <summary>
        /// 开始发送网络广播事件
        /// </summary>
        public static event SendDiscoveryBroadcastEventHandler StartSendDiscoveryBroadcast;

        /// <summary>
        /// 完成发送网络广播事件
        /// </summary>
        public static event SendDiscoveryBroadcastEventHandler CompleteSendDiscoveryBroadcast;


        private static CancellationTokenSource sendDiscoveryBroadcastCTS;
        private static CancellationTokenSource sendTimedDiscoveryBroadcastCTS;

        private static ChatBll chatBll;

        /// <summary>
        /// 已发送广播消息的IP集合
        /// </summary>
        private static List<Tuple<IPAddress, int>> broadcastAddresses;

        /// <summary>
        /// Nancy http监听服务
        /// </summary>
        private static NancyHost nancyHost;


        static ChatNetworkBll()
        {
            chatBll = new ChatBll();

            NetworkComms.DisableLogging();
            NetworkComms.CurrentRuntimeEnvironment = RuntimeEnvironment.Native_Net4;
            NetworkComms.ConnectionListenModeUseSync = true;

            HandlerDiscoveryMessage();//处理接收到的网络招手信息
            HandlerDiscoveryResponseMessage();//处理接收到的招手回应消息
            HandlerSayGoodByMessage();
        }

        #region UDP

        /// <summary>
        /// 在指定端口上启动UDP/http消息监听  
        /// </summary>
        /// <param name="port"></param>
        internal static void StartListen(int port)
        {
            var lanIpList = IpHelper.GetLANIPv4AddressList(false);
            foreach (var lanIp in lanIpList)
            {
                Connection.StartListening(ConnectionType.UDP, new IPEndPoint(IPAddress.Parse(lanIp), port));
            }

            #region 启动http消息监听服务
            var hostConfig = new HostConfiguration();
            hostConfig.UrlReservations.CreateAutomatically = true;
            nancyHost?.Dispose();
            nancyHost = new NancyHost(hostConfig, new Uri($"http://localhost:{port}"));
            nancyHost.Start();
            #endregion 启动http消息监听服务

        }


        /// <summary>
        /// 检查聊天服务运行状态
        /// </summary>
        /// <returns></returns>
        public static bool CheckChatServiceRunningState()
        {
            return Connection.ExistingLocalListenEndPoints(ConnectionType.UDP, new IPEndPoint(IPAddress.Any, Setting.Chat.Port)).Count > 0;
        }

        /// <summary>
        /// 定时发送网络发现广播消息
        /// </summary>
        internal static void TimedSendDiscoveryBroadcastToAllNetworkSegments()
        {
            sendTimedDiscoveryBroadcastCTS?.Cancel();
            sendTimedDiscoveryBroadcastCTS = new CancellationTokenSource();

            Task task = Task.Factory.StartNew(() =>
            {
                while (!sendTimedDiscoveryBroadcastCTS.IsCancellationRequested)
                {
                    try
                    {
                        SendDiscoveryBroadcastToAllNetworkSegments();
                    }
                    catch
                    { }

                    Thread.Sleep(30000);//每隔一分钟发送一次广播 60000
                }
            }, sendTimedDiscoveryBroadcastCTS.Token);
        }


        /// <summary>
        ///给所有网段内的设备发送网络发现广播
        /// </summary>
        /// <param name="broadcastAddresses"></param>
        /// <returns></returns>
        public static void SendDiscoveryBroadcastToAllNetworkSegments()
        {
            StartSendDiscoveryBroadcast?.Invoke(true, null);

            try
            {
                sendDiscoveryBroadcastCTS?.Cancel();
                sendDiscoveryBroadcastCTS = new CancellationTokenSource();

                broadcastAddresses = new List<Tuple<IPAddress, int>>();
                var addressList = IpHelper.GetBroadcastAddresses();//获取网段内的所有广播地址
                if (addressList != null && addressList.Count > 0)
                {
                    foreach (IPAddress iPAddress in addressList)
                    {
                        broadcastAddresses.Add(new Tuple<IPAddress, int>(iPAddress, Setting.Chat.Port));
                    }
                }
                else
                {
                    broadcastAddresses.Add(new Tuple<IPAddress, int>(IPAddress.Broadcast, Setting.Chat.Port));
                }

                chatBll.AllUserOffLine();//设置所有用户状态为离线

                List<Task> tasks = new List<Task>();

                foreach (var tuple in broadcastAddresses)
                {
                    if (sendDiscoveryBroadcastCTS.IsCancellationRequested) break;

                    Task task = Task.Factory.StartNew(() =>
                    {
                        try
                        {
                            SendDiscoveryBroadcastMessage(tuple.Item1, tuple.Item2);
                        }
                        catch
                        { }
                    }, sendDiscoveryBroadcastCTS.Token);

                    tasks.Add(task);
                }

                bool result = Task.WaitAll(tasks.ToArray(), 20000);//限制在20秒内完成广播，因为有时发送广播会卡住 10000

                CompleteSendDiscoveryBroadcast?.Invoke(result, result ? "已发送网络广播" : "发送网络广播超时");
            }
            catch (Exception ex)
            {
                LogHelper.WriteErrLog(ex, false);
                CompleteSendDiscoveryBroadcast?.Invoke(false, ex.Message);
            }
        }



        /// <summary>
        /// 根据指定的IP和端口发送网络发现广播消息
        /// </summary>
        /// <param name="iPAddress"></param>
        /// <param name="port"></param>
        /// <returns></returns>
        public static void SendDiscoveryBroadcastMessage(IPAddress iPAddress, int port)
        {
            SendUDPMessage(iPAddress, port, StringHelper.EncryptAndCompress(JsonHelper.SerializeObject(chatBll.GetCurrentUserImportantInfo())), "Discovery");
        }


        /// <summary>
        /// 发送UDP消息
        /// </summary>
        /// <param name="iPAddress"></param>
        /// <param name="port"></param>
        /// <param name="message"></param>
        /// <param name="messageType"></param>
        public static void SendUDPMessage(IPAddress iPAddress, int port, string message, string messageType = "Message")
        {
            // 创建一个UDP连接
            ConnectionInfo broadcastInfo = new ConnectionInfo(new IPEndPoint(iPAddress, port));
            // 使用 GetConnection 方法创建连接
            UDPConnection udpConnection = UDPConnection.GetConnection(broadcastInfo, UDPOptions.None);

            // 发送UDP消息
            udpConnection.SendObject(messageType, message);
        }


        /// <summary>
        /// 处理接收到的网络招手信息
        /// </summary>
        private static void HandlerDiscoveryMessage()
        {
            NetworkComms.AppendGlobalIncomingPacketHandler<string>("Discovery", (header, connection, userInfo) =>
            {
                if (connection.ConnectionInfo.RemoteEndPoint is IPEndPoint ipEndPoint)
                {
                    //忽略本机的广播消息
                    if (!IPAddress.IsLoopback(ipEndPoint.Address) && !IpHelper.GetLANIPv4AddressList(false).Any(it => it == ipEndPoint.Address.ToString()))
                    {
                        chatBll.ReceiveUserInfo(ipEndPoint.Address.ToString(), ipEndPoint.Port, userInfo);

                        //发送回应消息给广播消息的发送方 （UDP） I'm here
                        string message = StringHelper.EncryptAndCompress(JsonHelper.SerializeObject(chatBll.GetCurrentUserImportantInfo()));
                        SendUDPMessage(ipEndPoint.Address, ipEndPoint.Port, message, "DiscoveryResponse");
                    }
                }
            });
        }


        /// <summary>
        /// 处理接收到的招手 回应消息
        /// </summary>
        private static void HandlerDiscoveryResponseMessage()
        {
            NetworkComms.AppendGlobalIncomingPacketHandler<string>("DiscoveryResponse", (header, connection, userInfo) =>
            {
                if (connection.ConnectionInfo.RemoteEndPoint is IPEndPoint ipEndPoint)
                {
                    //忽略本机的广播消息
                    if (!IPAddress.IsLoopback(ipEndPoint.Address) && !IpHelper.GetLANIPv4AddressList(false).Any(it => it == ipEndPoint.Address.ToString()))
                    {
                        chatBll.ReceiveUserInfo(ipEndPoint.Address.ToString(), ipEndPoint.Port, userInfo);
                    }
                }
            });
        }


        /// <summary>
        /// 发送离线消息
        /// </summary>
        public static void SendSayGoodByMessage()
        {
            if (broadcastAddresses == null) return;

            List<Task> tasks = new List<Task>();

            try
            {
                foreach (var tuple in broadcastAddresses)
                {
                    Task task = Task.Factory.StartNew(() =>
                    {
                        try
                        {
                            // 创建一个UDP连接
                            ConnectionInfo broadcastInfo = new ConnectionInfo(new IPEndPoint(tuple.Item1, tuple.Item2));
                            // 使用 GetConnection 方法创建连接
                            UDPConnection udpConnection = UDPConnection.GetConnection(broadcastInfo, UDPOptions.None);

                            // 发送UDP消息
                            udpConnection.SendObject("SayGoodBy", Setting.Chat.UserId);
                        }
                        catch
                        { }
                    });

                    tasks.Add(task);
                }

                Task.WaitAll(tasks.ToArray(), 2000);
            }
            catch
            {
            }
        }


        /// <summary>
        /// 处理离线消息
        /// </summary>
        private static void HandlerSayGoodByMessage()
        {
            NetworkComms.AppendGlobalIncomingPacketHandler<string>("SayGoodBy", (header, connection, userId) =>
            {
                if (connection.ConnectionInfo.RemoteEndPoint is IPEndPoint ipEndPoint)
                {
                    //忽略本机的广播消息
                    if (!IPAddress.IsLoopback(ipEndPoint.Address) && !IpHelper.GetLANIPv4AddressList(false).Any(it => it == ipEndPoint.Address.ToString()))
                    {
                        chatBll.SetUserLineState(userId, false);//设置用户状态为离线
                    }
                }
            });
        }

        #endregion UDP



        /// <summary>
        /// 退出
        /// </summary>
        public static void Exit()
        {
            try
            {
                //停止定时广播后台任务
                sendDiscoveryBroadcastCTS?.Cancel();
                sendTimedDiscoveryBroadcastCTS?.Cancel();
            }
            catch { }

            try
            {
                SendSayGoodByMessage();//说再见
            }
            catch { }

            try
            {
                nancyHost?.Dispose();//停止http监听服务
            }
            catch { }

            try
            {
                NetworkComms.Shutdown();//停止udp监听
            }
            catch { }
        }

    }

}
